//package com.geely.pma.climate.api
package com.wanghaibo.vgashow.api;

import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import com.ecarx.xui.adaptapi.FunctionStatus
import com.ecarx.xui.adaptapi.Operator
import com.ecarx.xui.adaptapi.car.base.ICarFunction
import com.ecarx.xui.adaptapi.car.base.ICarFunction.IFunctionValueWatcher
import com.ecarx.xui.adaptapi.car.hvac.IFragrance
import com.ecarx.xui.adaptapi.car.hvac.IHvac
import com.ecarx.xui.adaptapi.car.vehicle.IBcm
import com.ecarx.xui.adaptapi.car.vehicle.IDayMode
import com.ecarx.xui.adaptapi.car.vehicle.IUnits
import com.ecarx.xui.adaptapi.car.vehicle.IVehicle
import com.ecarx.xui.adaptapi.vehicle.VehicleZone
import com.wanghaibo.vgashow.BuildConfig
//import com.geely.pma.climate.BuildConfig
//import com.geely.pma.util.climatecrashinfo.CarAdapterApiExceptionHandler.saveMessage
import java.util.*

/**
 * Name: ICarFunctionProxy
 *
 * Created by Wuhao on 2019/12/9
 *
 * Version: 0.0.1
 */
internal class CarFunctionProxy(private val carFunction: ICarFunction) : ICarFunction {

    private val maps by lazy { HashMap<IFunctionValueWatcher, FunctionValueWatcher>() }

    @RequiresApi(Build.VERSION_CODES.N)
    companion object {
        private const val TAG = "CarFunctionProxy"
        private val FUNC_NAMES: MutableMap<Int, String>
        private val CONSTANT_NAMES: MutableMap<Int, String>
        private val ZONE_NAMES: MutableMap<Int, String>
        private val OPERATOR_NAMES: MutableMap<Int, String>

        private fun toFunctionText(function: Int) =
            FUNC_NAMES[function] ?: String.format("0x%08X", function)

        private fun toZoneText(zone: Int) =
            ZONE_NAMES[zone] ?: String.format("0x%08X", zone)

        private fun toOperatorText(operator: Int) =
            OPERATOR_NAMES[operator] ?: String.format("0x%08X", operator)

        private fun toValueText(value: Int) =
            CONSTANT_NAMES[value] ?: String.format("0x%08X", value)

        private fun toFloatValueText(value: Float) =
            if (value != Float.MIN_VALUE) value.toString() else "ERROR"

        init {
            FUNC_NAMES = HashMap()
            CONSTANT_NAMES = HashMap()
            // fetch function names and constant names by common prefixes
            val classes = arrayOf(
                IHvac::class.java, IFragrance::class.java, IBcm::class.java, IUnits::class.java,
                IDayMode::class.java
            )
            val prefixes = arrayOf(
                "HVAC_FUNC_", "HVAC_FUNC_", "BCM_FUNC_", "FUNC_UNIT_", "SETTING_FUNC_"
            )
            for (i in classes.indices) {
                for (field in classes[i].fields) {
                    try {
                        if (Int::class.javaPrimitiveType == field.type) {
                            val name: String = field.name
                            if (name.startsWith(prefixes[i])) {
                                FUNC_NAMES[field.getInt(null)] = name
                            } else {
                                CONSTANT_NAMES.putIfAbsent(field.getInt(null), name)
                            }
                        }
                    } catch (e: Exception) {
                        e.printStackTrace()
//                        saveMessage(e.message!!)
                    }
                }
            }
            // fetch constant names with prefix "COMMON_VALUE_"
            for (field in ICarFunction::class.java.fields) {
                try {
                    if (Int::class.javaPrimitiveType == field.type) {
                        val name: String = field.name
                        if (name.startsWith("COMMON_VALUE_")) {
                            CONSTANT_NAMES[field.getInt(null)] = name
                        }
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
//                    saveMessage(e.message!!)
                }
            }
            // fetch constant names with prefix "DAYMODE_SETTING_" except "DAYMODE_SETTING_OFF"
            for (field in IVehicle::class.java.fields) {
                try {
                    if (Int::class.javaPrimitiveType == field.type) {
                        val name: String = field.name
                        if (name.startsWith("DAYMODE_SETTING_")
                            && name != "DAYMODE_SETTING_OFF"
                        ) {
                            CONSTANT_NAMES[field.getInt(null)] = name
                        }
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
//                    saveMessage(e.message!!)
                }
            }

            // init zone names
            ZONE_NAMES = HashMap()
            for (field in VehicleZone::class.java.fields) {
                try {
                    if (Int::class.javaPrimitiveType == field.type) {
                        ZONE_NAMES[field.getInt(null)] = field.name
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
//                    saveMessage(e.message!!)
                }
            }

            // init operator names
            OPERATOR_NAMES = HashMap()
            for (field in Operator::class.java.fields) {
                try {
                    if (Int::class.javaPrimitiveType == field.type) {
                        OPERATOR_NAMES[field.getInt(null)] = field.name
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
//                    saveMessage(e.message!!)
                }
            }
        }
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun isFunctionSupported(function: Int): FunctionStatus {
        var result: FunctionStatus? = null
        try {
            //EXCEPTION f) Inject isFunctionSupported in the debug mode to return a mock value
//            if (BuildConfig.DEBUG) {
//                return functionStatusFromGradle
//            }
            result = carFunction.isFunctionSupported(function)
            Log.i(TAG, "isFunctionSupported(" + toFunctionText(function) + ") = " + result)
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result!!
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun isFunctionSupported(function: Int, zone: Int): FunctionStatus {
        var result: FunctionStatus? = null
        try {
            //EXCEPTION f) Inject isFunctionSupported in the debug mode to return a mock value
//            if (BuildConfig.DEBUG) {
//                return functionStatusFromGradle
//            }
            result = carFunction.isFunctionSupported(function, zone)
            Log.i(
                TAG, "isFunctionSupported(" + toFunctionText(function) + ", "
                        + toZoneText(zone) + ") = " + result
            )
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result!!
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun isFunctionSupported(function: Int, zone: Int, operator: Int): FunctionStatus {
        var result: FunctionStatus? = null
        try {
            //EXCEPTION f) Inject isFunctionSupported in the debug mode to return a mock value
//            if (BuildConfig.DEBUG) {
//                return functionStatusFromGradle
//            }
            result = carFunction.isFunctionSupported(function, zone, operator)
            Log.i(
                TAG, "isFunctionSupported(" + toFunctionText(function) + ", "
                        + toZoneText(zone) + ", " + toOperatorText(operator) + ") = " + result
            )
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result!!
    }

    // -1 -> error, 1 -> notavailable, 2 -> notactive, 3 -> active
//    private val functionStatusFromGradle: FunctionStatus =
//        when (BuildConfig.functionStatus) {
//            1 -> FunctionStatus.notavailable
//            2 -> FunctionStatus.notactive
//            3 -> FunctionStatus.active
//            else -> FunctionStatus.error
//        }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun getSupportedFunctionValue(function: Int): IntArray {
        try {
            val result = carFunction.getSupportedFunctionValue(function)
            val text = StringBuilder("[")
            for (i in result.indices) {
                if (i > 0) {
                    text.append(", ")
                }
                text.append(result[i]).append(" : ").append(toValueText(result[i]))
            }
            text.append("]")
            Log.i(
                TAG, "getSupportedFunctionValue(" + toFunctionText(function) + ") = "
                        + text.toString()
            )
            return result
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return IntArray(0)
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun setFunctionValue(function: Int, funcValue: Int): Boolean {
        var result = false
        try {
            result = carFunction.setFunctionValue(function, funcValue)
            Log.i(
                TAG, "setFunctionValue(" + toFunctionText(function) + ", "
                        + toValueText(funcValue) + ") = " + result
            )
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun setFunctionValue(function: Int, zone: Int, funcValue: Int): Boolean {
        var result = false
        try {
            result = carFunction.setFunctionValue(function, zone, funcValue)
            Log.i(
                TAG, "setFunctionValue(" + toFunctionText(function) + ", "
                        + toZoneText(zone) + ", " + toValueText(funcValue) + ") = " + result
            )
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun getFunctionValue(function: Int): Int {
        var result = ICarFunction.COMMON_VALUE_ERROR
        try {
            result = carFunction.getFunctionValue(function)
            Log.i(
                TAG, "getFunctionValue(" + toFunctionText(function) + ") = " + toValueText(result)
            )
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun getFunctionValue(function: Int, zone: Int): Int {
        var result = ICarFunction.COMMON_VALUE_ERROR
        try {
            result = carFunction.getFunctionValue(function, zone)
            Log.i(
                TAG, "getFunctionValue(" + toFunctionText(function) + ", "
                        + toZoneText(zone) + ") = " + toValueText(result)
            )
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun setCustomizeFunctionValue(function: Int, value: Float): Boolean {
        var result = false
        try {
            result = carFunction.setCustomizeFunctionValue(function, value)
            Log.i(
                TAG, "setCustomizeFunctionValue(" + toFunctionText(function) + ", "
                        + toFloatValueText(value) + ") = " + result
            )
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun setCustomizeFunctionValue(function: Int, zone: Int, value: Float): Boolean {
        var result = false
        try {
            result = carFunction.setCustomizeFunctionValue(function, zone, value)
            Log.i(
                TAG, "setCustomizeFunctionValue(" + toFunctionText(function) + ", "
                        + toZoneText(zone) + ", " + toFloatValueText(value) + ") = " + result
            )
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun getCustomizeFunctionValue(function: Int): Float {
        var result = Float.MIN_VALUE
        try {
            result = carFunction.getCustomizeFunctionValue(function)
            Log.i(
                TAG, "getCustomizeFunctionValue(" + toFunctionText(function) + ") = "
                        + toFloatValueText(result)
            )
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun getCustomizeFunctionValue(function: Int, zone: Int): Float {
        var result = Float.MIN_VALUE
        try {
            result = carFunction.getCustomizeFunctionValue(function, zone)
            Log.i(
                TAG, "getCustomizeFunctionValue(" + toFunctionText(function) + ", "
                        + toZoneText(zone) + ") = " + toFloatValueText(result)
            )
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result
    }

    override fun registerFunctionValueWatcher(watcher: IFunctionValueWatcher?): Boolean {
        var result = false
        try {
            var functionValueWatcher = maps[watcher]
            if (functionValueWatcher == null && watcher != null) {
                functionValueWatcher = FunctionValueWatcher(watcher)
                maps[watcher] = functionValueWatcher
            }
            result = carFunction.registerFunctionValueWatcher(functionValueWatcher)
            Log.i(TAG, "registerFunctionValueWatcher($watcher) = $result")
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun registerFunctionValueWatcher(
        function: Int, watcher: IFunctionValueWatcher?
    ): Boolean {
        var result = false
        try {
            var functionValueWatcher = maps[watcher]
            if (functionValueWatcher == null && watcher != null) {
                functionValueWatcher = FunctionValueWatcher(watcher)
                maps[watcher] = functionValueWatcher
            }
            result = carFunction.registerFunctionValueWatcher(function, functionValueWatcher)
            Log.i(
                TAG, "registerFunctionValueWatcher(" + toFunctionText(function) + ", " +
                        watcher + ") = " + result
            )
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun registerFunctionValueWatcher(
        functions: IntArray, watcher: IFunctionValueWatcher?
    ): Boolean {
        var result = false
        try {
            var functionValueWatcher = maps[watcher]
            if (functionValueWatcher == null && watcher != null) {
                functionValueWatcher = FunctionValueWatcher(watcher)
                maps[watcher] = functionValueWatcher
            }
            result = carFunction.registerFunctionValueWatcher(functions, functionValueWatcher)
            val text = StringBuilder("[")
            for (i in functions.indices) {
                if (i > 0) {
                    text.append(", ")
                }
                text.append(toFunctionText(functions[i]))
            }
            text.append("]")
            Log.i(
                TAG, "registerFunctionValueWatcher(" + text.toString() + ", "
                        + watcher + ") = " + result
            )
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result
    }

    override fun unregisterFunctionValueWatcher(watcher: IFunctionValueWatcher?): Boolean {
        var result = false
        try {
            result = carFunction.unregisterFunctionValueWatcher(maps.remove(watcher))
            Log.i(TAG, "unregisterFunctionValueWatcher($watcher) = $result")
        } catch (e: Exception) {
            e.printStackTrace()
//            saveMessage(e.message!!)
        }
        return result
    }

    private class FunctionValueWatcher internal constructor(private val functionValueWatcher: IFunctionValueWatcher) :
        IFunctionValueWatcher {
        @RequiresApi(Build.VERSION_CODES.N)
        override fun onFunctionChanged(function: Int) {
            try {
                Log.i(TAG, "onFunctionChanged(" + toFunctionText(function) + ")")
                functionValueWatcher.onFunctionChanged(function)
            } catch (e: Exception) {
                e.printStackTrace()
//                saveMessage(e.message!!)
            }
        }

        @RequiresApi(Build.VERSION_CODES.N)
        override fun onFunctionValueChanged(function: Int, zone: Int, value: Int) {
            try {
                Log.i(
                    TAG, "onFunctionValueChanged(" + toFunctionText(function) + ", "
                            + toZoneText(zone) + ", " + toValueText(value) + ")"
                )
                functionValueWatcher.onFunctionValueChanged(function, zone, value)
            } catch (e: Exception) {
                e.printStackTrace()
//                saveMessage(e.message!!)
            }
        }

        @RequiresApi(Build.VERSION_CODES.N)
        override fun onCustomizeFunctionValueChanged(function: Int, zone: Int, value: Float) {
            try {
                Log.i(
                    TAG, "onCustomizeFunctionValueChanged(" + toFunctionText(function) + ", " +
                            toZoneText(zone) + ", " + toFloatValueText(value) + ")"
                )
                functionValueWatcher.onCustomizeFunctionValueChanged(function, zone, value)
            } catch (e: Exception) {
                e.printStackTrace()
//                saveMessage(e.message!!)
            }
        }

        @RequiresApi(Build.VERSION_CODES.N)
        override fun onSupportedFunctionStatusChanged(
            function: Int, zone: Int, functionStatus: FunctionStatus
        ) {
            try {
                //EXCEPTION f) disable onSupportedFunctionStatusChanged in the debug mode
                if (BuildConfig.DEBUG) {
                    return
                }
                Log.i(
                    TAG, "onSupportedFunctionStatusChanged(" + toFunctionText(function)
                            + ", " + toZoneText(zone) + ", " + functionStatus + ")"
                )
                functionValueWatcher.onSupportedFunctionStatusChanged(
                    function, zone, functionStatus
                )
            } catch (e: Exception) {
                e.printStackTrace()
//                saveMessage(e.message!!)
            }
        }

        @RequiresApi(Build.VERSION_CODES.N)
        override fun onSupportedFunctionValueChanged(function: Int, funcValues: IntArray) {
            try {
                val text = StringBuilder("[")
                for (i in funcValues.indices) {
                    if (i > 0) {
                        text.append(", ")
                    }
                    text.append(funcValues[i]).append(" : ").append(toValueText(funcValues[i]))
                }
                text.append("]")
                Log.i(
                    TAG, "onSupportedFunctionValueChanged(" + toFunctionText(function)
                            + ", " + text.toString() + ")"
                )
                functionValueWatcher.onSupportedFunctionValueChanged(function, funcValues)
            } catch (e: Exception) {
                e.printStackTrace()
//                saveMessage(e.message!!)
            }
        }

    }
}